
 <!DOCTYPE HTML>
<html lang="zh-Hans">
<head>
  <meta charset="UTF-8">
  
    <title>使用 flying 完善Mybatis 自带的二级缓存 | my bat is flying in sunny-spring</title>
    <meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
    
    <meta name="author" content="limeng32">
    

    
    <meta name="description" content="本节内容向您讲解如何使用 EnhancedCachingInterceptor 拦截器来改造Mybatis的二级缓存使其可用。">
<meta property="og:type" content="article">
<meta property="og:title" content="使用 flying 完善Mybatis 自带的二级缓存">
<meta property="og:url" content="http://flying-doc.limeng32.com/2017/04/01/2017-04-01-使用flying完善Mybatis自带的二级缓存/index.html">
<meta property="og:site_name" content="my bat is flying in sunny-spring">
<meta property="og:description" content="本节内容向您讲解如何使用 EnhancedCachingInterceptor 拦截器来改造Mybatis的二级缓存使其可用。">
<meta property="og:locale" content="zh-Hans">
<meta property="og:updated_time" content="2018-01-21T14:00:23.418Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="使用 flying 完善Mybatis 自带的二级缓存">
<meta name="twitter:description" content="本节内容向您讲解如何使用 EnhancedCachingInterceptor 拦截器来改造Mybatis的二级缓存使其可用。">

    
    <link rel="alternative" href="/atom.xml" title="my bat is flying in sunny-spring" type="application/atom+xml">
    
    
    
    <link rel="stylesheet" href="/css/style.css">
</head>

  <body>
    <header>
      
<div>
		
			<div id="textlogo">
				<h1 class="site-name"><a href="/" title="my bat is flying in sunny-spring">my bat is flying in sunny-spring</a></h1>
				<h2 class="blog-motto"></h2>
			</div>
			<div class="navbar"><a class="navbutton navmobile" href="#" title="菜單">
			</a></div>
			<nav class="animated">
				<ul>
					<ul>
					 
						<li><a href="/">Home</a></li>
					
						<li><a href="/archives">Archives</a></li>
					
						<li><a href="/about">About</a></li>
					
					<li>
 					
					<form class="search" action="//google.com/search" method="get" accept-charset="utf-8">
						<label>Search</label>
						<input type="search" id="search" name="q" autocomplete="off" maxlength="20" placeholder="搜索" />
						<input type="hidden" name="q" value="site:flying-doc.limeng32.com">
					</form>
					
					</li>
				</ul>
			</nav>			
</div>
    </header>
    <div id="container">
      <div id="main" class="post" itemscope itemprop="blogPost">
  
	<article itemprop="articleBody"> 
		<header class="article-info clearfix">
  <h1 itemprop="name">
    
      <a href="/2017/04/01/2017-04-01-使用flying完善Mybatis自带的二级缓存/" title="使用 flying 完善Mybatis 自带的二级缓存" itemprop="url">使用 flying 完善Mybatis 自带的二级缓存</a>
  </h1>
  <p class="article-author">By
       
		<a href="/about" title="limeng32" target="_blank" itemprop="author">limeng32</a>
		
  <p class="article-time">
    <time datetime="2017-03-31T16:00:00.000Z" itemprop="datePublished"> 發表於 2017-04-01</time>
    
  </p>
</header>
	<div class="article-content">
		
		<div id="toc" class="toc-article">
			<strong class="toc-title">文章目錄</strong>
		
		<ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#上手指南"><span class="toc-text"><a href="#&#x4E0A;&#x624B;&#x6307;&#x5357;" class="headerlink" title="&#x4E0A;&#x624B;&#x6307;&#x5357;"></a><a href="#&#x4E0A;&#x624B;&#x6307;&#x5357;">&#x4E0A;&#x624B;&#x6307;&#x5357;</a></span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#观察者-amp-触发者"><span class="toc-text"><a href="#&#x89C2;&#x5BDF;&#x8005;-amp-&#x89E6;&#x53D1;&#x8005;" class="headerlink" title="&#x89C2;&#x5BDF;&#x8005; &amp; &#x89E6;&#x53D1;&#x8005;"></a><a href="#&#x89C2;&#x5BDF;&#x8005;-amp-&#x89E6;&#x53D1;&#x8005;">&#x89C2;&#x5BDF;&#x8005; &amp; &#x89E6;&#x53D1;&#x8005;</a></span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#跨数据源缓存"><span class="toc-text"><a href="#&#x8DE8;&#x6570;&#x636E;&#x6E90;&#x7F13;&#x5B58;" class="headerlink" title="&#x8DE8;&#x6570;&#x636E;&#x6E90;&#x7F13;&#x5B58;"></a><a href="#&#x8DE8;&#x6570;&#x636E;&#x6E90;&#x7F13;&#x5B58;">&#x8DE8;&#x6570;&#x636E;&#x6E90;&#x7F13;&#x5B58;</a></span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#注意事项"><span class="toc-text"><a href="#&#x6CE8;&#x610F;&#x4E8B;&#x9879;" class="headerlink" title="&#x6CE8;&#x610F;&#x4E8B;&#x9879;"></a><a href="#&#x6CE8;&#x610F;&#x4E8B;&#x9879;">&#x6CE8;&#x610F;&#x4E8B;&#x9879;</a></span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#flying-如何判断缓存是否命中"><span class="toc-text"><a href="#flying-&#x5982;&#x4F55;&#x5224;&#x65AD;&#x7F13;&#x5B58;&#x662F;&#x5426;&#x547D;&#x4E2D;" class="headerlink" title="flying &#x5982;&#x4F55;&#x5224;&#x65AD;&#x7F13;&#x5B58;&#x662F;&#x5426;&#x547D;&#x4E2D;"></a><a href="#flying-&#x5982;&#x4F55;&#x5224;&#x65AD;&#x7F13;&#x5B58;&#x662F;&#x5426;&#x547D;&#x4E2D;">flying &#x5982;&#x4F55;&#x5224;&#x65AD;&#x7F13;&#x5B58;&#x662F;&#x5426;&#x547D;&#x4E2D;</a></span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#flying-已回滚的操作是否会生成缓存"><span class="toc-text"><a href="#flying-&#x5DF2;&#x56DE;&#x6EDA;&#x7684;&#x64CD;&#x4F5C;&#x662F;&#x5426;&#x4F1A;&#x751F;&#x6210;&#x7F13;&#x5B58;" class="headerlink" title="flying &#x5DF2;&#x56DE;&#x6EDA;&#x7684;&#x64CD;&#x4F5C;&#x662F;&#x5426;&#x4F1A;&#x751F;&#x6210;&#x7F13;&#x5B58;"></a><a href="#flying-&#x5DF2;&#x56DE;&#x6EDA;&#x7684;&#x64CD;&#x4F5C;&#x662F;&#x5426;&#x4F1A;&#x751F;&#x6210;&#x7F13;&#x5B58;">flying &#x5DF2;&#x56DE;&#x6EDA;&#x7684;&#x64CD;&#x4F5C;&#x662F;&#x5426;&#x4F1A;&#x751F;&#x6210;&#x7F13;&#x5B58;</a></span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#附录"><span class="toc-text"><a href="#&#x9644;&#x5F55;" class="headerlink" title="&#x9644;&#x5F55;"></a><a href="#&#x9644;&#x5F55;">&#x9644;&#x5F55;</a></span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#代码示例"><span class="toc-text"><a href="#&#x4EE3;&#x7801;&#x793A;&#x4F8B;" class="headerlink" title="&#x4EE3;&#x7801;&#x793A;&#x4F8B;"></a><a href="#&#x4EE3;&#x7801;&#x793A;&#x4F8B;">&#x4EE3;&#x7801;&#x793A;&#x4F8B;</a></span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#account-表建表语句"><span class="toc-text"><a href="#account-&#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;" class="headerlink" title="account &#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;"></a><a href="#account-&#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;">account &#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;</a></span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#role-表建表语句"><span class="toc-text"><a href="#role-&#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;" class="headerlink" title="role &#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;"></a><a href="#role-&#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;">role &#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;</a></span></a></li></ol></li></ol>
		
		</div>
		
		<h2 id="上手指南"><a href="#上手指南" class="headerlink" title="上手指南"></a><a href="#上手指南">上手指南</a></h2><p>上一篇文章中我们介绍了使用 flying 解决 pojo 自动映射问题，在本篇文章中我们介绍如何使用 flying 优化后的 mybatis 自带二级缓存。mybatis 自带的二级缓存方便上手容易使用，并且如果您把它和 redis 等第三方缓存结合使用，它就能成为可扩展的强大缓存；如果您的系统用户数不到一千并且您不想为缓存配置额外的服务器，那您也可以只使用 mybatis 的二级缓存，因为它的使用成本极低。</p>
<p>mybatis 的二级缓存只存在于内存中，不会写入硬盘，但可以通过 redis 来实现数据落地，在使用前请确保您的业务可以接受这些特性。</p>
<p>为使用 flying 优化后的 mybatis 自带二级缓存，您只需要在 Configuration.xml 和 <i>pojo_mapper</i>.java 中进行简单的配置。在 Configuration.xml 中配置的内容是：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">settings</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">"cacheEnabled"</span> <span class="attr">value</span>=<span class="string">"true"</span> /&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">"lazyLoadingEnabled"</span> <span class="attr">value</span>=<span class="string">"false"</span> /&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">"aggressiveLazyLoading"</span> <span class="attr">value</span>=<span class="string">"false"</span> /&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">"localCacheScope"</span> <span class="attr">value</span>=<span class="string">"SESSION"</span> /&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">"autoMappingBehavior"</span> <span class="attr">value</span>=<span class="string">"PARTIAL"</span> /&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">"lazyLoadTriggerMethods"</span> <span class="attr">value</span>=<span class="string">""</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">settings</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>需要注意的是 <code>settings</code> 中第一行 <code>cacheEnabled</code> 的值为 <code>true</code>，其它内容都和 <a href="http://flying-doc.limeng32.com/2017/06/01/2017-06-01-flying%20%E6%98%AF%E4%BB%80%E4%B9%88/">《flying-是什么》</a> 中介绍的配置完全一致。</p>
<p>在完成上述工作后，mybatis 的二级缓存就可以使用了。现在启动项目后，每次对数据库发起的查询请求都会被缓存进行拦截，如果在缓存中能找到结果（包括单个 pojo、pojo集合、数量等）就直接返回结果，不会对数据库产生压力；如果找不到结果再去数据库中进行查询，查询后返回结果的同时把此次结果记入缓存中，这样下次再进行相同条件查询时如果相关记录没进行过刷新型操作（如 update、delete），就会返回缓存中的结果；如果对某条数据进行了 update、delete 操作，会使得相关缓存失效，其中的机制在后面会有详细介绍。</p>
<p>由于服务容器刚启动时缓存中没有任何内容，因此所有查询第一次都会经过数据库，在此之后缓存将发挥作用。为了更好的说明，我们需要新建<a href="#AccountTableCreater">一个账号表</a>和<a href="#RoleTableCreater">一个角色表</a>、相关的 <code>account.xml</code>、<code>role.xml</code>、<code>AccountMapper.java</code>、<code>RoleMapper.java</code>、<code>AccountService.java</code>、<br><code>RoleService.java</code>、<code>Account.java</code> 以及 <code>Role.java</code>。</p>
<p><code>AccountMapper.java</code> 如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> myPackage;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">AccountMapper</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Account <span class="title">select</span><span class="params">(Object id)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Account <span class="title">selectOne</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Collection&lt;Account&gt; <span class="title">selectAll</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">count</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">insert</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">update</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">updatePersistent</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">delete</span><span class="params">(Account t)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p><code>RoleMapper.java</code> 如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> myPackage;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">RoleMapper</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Role <span class="title">select</span><span class="params">(Object id)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Role <span class="title">selectOne</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Collection&lt;Role&gt; <span class="title">selectAll</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">count</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">insert</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">update</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">updatePersistent</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">delete</span><span class="params">(Role t)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p><code>account.xml</code>、<code>role.xml</code>、<code>AccountService.java</code>、<code>RoleService.java</code>、<code>Account.java</code> 和 <code>Role.java</code> 与 <a href="http://flying-doc.limeng32.com/2017/05/01/2017-05-01-%E4%BD%BF%E7%94%A8flying%E8%A7%A3%E5%86%B3pojo%E8%87%AA%E5%8A%A8%E6%98%A0%E5%B0%84%E9%97%AE%E9%A2%98/">《使用 flying 解决 pojo 自动映射问题》</a> 中介绍的完全一致且不会在本文中进行修改，因此这里不再累述。</p>
<p>之后，我们再定义一个用于测试的数据集：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dataset</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">account</span> <span class="attr">account_id</span>=<span class="string">"1"</span> <span class="attr">fk_role_id</span>=<span class="string">"10"</span> <span class="attr">address</span>=<span class="string">"beijing"</span> <span class="attr">name</span>=<span class="string">"frank"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">account</span> <span class="attr">account_id</span>=<span class="string">"2"</span> <span class="attr">fk_role_id</span>=<span class="string">"11"</span> <span class="attr">address</span>=<span class="string">"tianjin"</span> <span class="attr">name</span>=<span class="string">"gale"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">account</span> <span class="attr">account_id</span>=<span class="string">"3"</span> <span class="attr">fk_role_id</span>=<span class="string">"11"</span> <span class="attr">address</span>=<span class="string">"beijing"</span> <span class="attr">name</span>=<span class="string">"hank"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">role</span> <span class="attr">role_id</span>=<span class="string">"10"</span> <span class="attr">role_name</span>=<span class="string">"user"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">role</span> <span class="attr">role_id</span>=<span class="string">"11"</span> <span class="attr">role_name</span>=<span class="string">"super_user"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dataset</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>当我们第一次执行 <code>accountService.select(1)</code> 时，会从数据库中得到一个 <code>name</code> 为 “frank” 的 Account 对象，当再次执行 <code>accountService.select(1)</code> 时，会不经数据库而从缓存中得到这个 Account 对象。如果我们通过数据库管理工具直接修改了这条记录，比如将 <code>name</code> 改为 “iris”，缓存也不会知道我们做出了改动，再次执行 <code>accountService.select(1)</code> 得到的 pojo 的 <code>name</code> 依然为 “frank”。但接下来如果我们在程序中执行 <code>accountService.update(account)</code> 将此对象的 <code>name</code> 改为 “hank”，则在数据库中的记录变更后缓存中的对象也会刷新，下一次再执行 <code>accountService.select(1)</code> 得到的 Account 对象的 <code>name</code> 就是 “hank” 了。</p>
<p>最后，我们强烈不建议在开启缓存的项目正在运行的情况下，通过数据库管理工具直接修改数据，这会导致数据库和缓存不一致，就像上一段中演示的那样。当您得到缓存的好处时，您也只能使用已经定义好的方法来操作数据库。（实际上，我们不建议通过数据库管理工具直接修改任何正在运行中的系统，无论它是否使用了缓存。）</p>
<h2 id="观察者-amp-触发者"><a href="#观察者-amp-触发者" class="headerlink" title="观察者 &amp; 触发者"></a><a href="#观察者-amp-触发者">观察者 &amp; 触发者</a></h2><p>在上一节中我们了解了 mybatis 二级缓存的原理，但它本身还有需要优化的地方。例如当您缓存了一个 Account 对象后，当这个对象对应的 Role 对象发生改变时，我们当然希望缓存中的 Account 对象会知道它的多对一关系 Role 对象已经失效，然后在查询时会到数据库中查找最新数据（因为查询子对象时会自动加载父对象）。然而遗憾的是，mybatis 原始的二级缓存并没有这个功能，但是 flying 提供了解决方案。您只需将 flying 优化缓存的插件配置好并了解 “观察者” 和 “触发者” 的概念，就可以解决这一问题。</p>
<p>首先在 <code>Configuration.xml</code> 中的 <code>plugins</code> 中加入以下内容：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugin</span> <span class="attr">interceptor</span>=<span class="string">"indi.mybatis.flying.interceptors.EnhancedCachingInterceptor"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"cacheEnabled"</span> <span class="attr">value</span>=<span class="string">"true"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"annotationPackage"</span> <span class="attr">value</span>=<span class="string">"myPackage"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>其中 <code>cacheEnabled</code> 为 <code>true</code> 表示让优化插件生效，<code>annotationPackage</code> 的值则是放置您所有 <i>pojo_mapper</i>.java 接口的包路径。如果您的 <i>pojo_mapper</i>.java 在多个包下（例如您在项目中用 mybatis 管理多个数据源，每个数据源的 <i>pojo_mapper</i>.java 放在不同的包内），这里可以配置多个包路径，只需用英文逗号分开即可，如 <code>value=&quot;myPackage,myPackage2&quot;</code> 这样。如果您有未声明的 <i>pojo_mapper</i>.java，则该 pojo 对应的缓存不会被优化。</p>
<p>然后我们来介绍<b>观察者</b>和<b>触发者</b>的概念。在有父子关系的两个 pojo 中，父对象自身的改变可以使子对象的缓存失效，因此父对象可以称作子对象的<b>触发者</b>，而因为子对象只能参考它的父对象，子对象又可称为父对象的<b>观察者</b>。值得一提的是，这两种称呼都是基于 pojo 的关系的。所以会出现这种情况：pojo A 是 B 的触发者，pojo B 是 A 的观察者；同时 pojo B 是 C 的触发者，pojo C 是 B 的观察者。触发-观察关系可以传递，因此可以称 pojo A 是 C 的<b>间接触发者</b>，pojo C 是 A 的<b>间接观察者</b>。（如果两个 pojo 间有 触发-观察 关系但又不是直接关联，那就称为 间接触发-间接观察 关系，稍后您就后明白为什么要这么定义）。</p>
<p>接下来我们还需要了解触发者有哪些方法可以“触发”观察者，由之前的文章可以知道，<code>update</code>、<code>delete</code> 会使观察者改变，<code>select</code> 不会改变观察者，而 <code>insert</code> 是增加新的父对象，也不会改变观察者。了解以上概念后，我们就需要让程序了解我们的对象关系模型，而 flying 选择在 <i>pojo_mapper</i>.java 接口中接收您描述的对象关系模型。</p>
<p>我们只需在 AccountMapper.java 中增加一些注解：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> myPackage;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.CacheAnnotation;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.CacheRoleAnnotation;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.statics.CacheRoleType;</span><br><span class="line"></span><br><span class="line"><span class="meta">@CacheRoleAnnotation</span>(ObserverClass = &#123; Role.class &#125;, TriggerClass = &#123; Account.class &#125;)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">AccountMapper</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Observer)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Account <span class="title">select</span><span class="params">(Object id)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Observer)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Account <span class="title">selectOne</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Observer)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Collection&lt;Account&gt; <span class="title">selectAll</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Observer)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">count</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">insert</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Trigger)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">update</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Trigger)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">updatePersistent</span><span class="params">(Account t)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Trigger)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">delete</span><span class="params">(Account t)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>接下来我们对这些注解依次说明：<br><code>@CacheRoleAnnotation</code> 只能放在类定义之上，它声明当前 <i>pojo_mapper</i>.java 接口对应的 pojo 和其它 pojo 的关系，具体来说，<code>ObserverClass</code> 描述的是当前 pojo 是哪些 pojo 的观察者，因此需要填写当前 pojo 的所有<b>直接触发者</b>的类名（无需填写间接触发者，因为 flying 在运行期可以推导出），<code>TriggerClass</code> 则<b>只需填写当前接口对应的 pojo 类名</b>。填好以后 <code>@CacheRoleAnnotation</code> 就配置完毕。</p>
<p><code>@CacheAnnotation</code> 只能放在方法定义之上，它声明当前这个方法是具有“触发”能力还是具有“观察”能力。具有触发能力的方法可以刷新这个类的观察者的缓存，具有观察能力的方法可以发现自己的缓存已被刷新，因为 触发－观察 关系可以传递，最终所有 pojo 的缓存关系都会被 flying 所获知。</p>
<p>在 flying 给定的方法中，<code>update</code>、<code>updatePersistent</code>、<code>delete</code> 具有触发能力，<code>select</code>、<code>selectOne</code>、<code>selectAll</code>、<code>count</code> 具有观察能力，所以要在相应的方法上配置 <code>@CacheRoleAnnotation</code> 注解。<code>insert</code> 既不具有触发能力也不具有观察能力。如果您在 <i>pojo_mapper</i>.java 中还有自己定义的方法，需要看这个方法对应的 sql 语句类型来进行配置，如果是 update 或 delete 类型就具有触发能力，如果是 select 类型就具有观察能力。</p>
<p>同样，RoleMapper.java 中也要增加一些注解：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> myPackage;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.CacheAnnotation;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.CacheRoleAnnotation;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.statics.CacheRoleType;</span><br><span class="line"></span><br><span class="line"><span class="meta">@CacheRoleAnnotation</span>(ObserverClass = &#123;&#125;, TriggerClass = &#123; Role.class &#125;)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">RoleMapper</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Observer)</span><br><span class="line">	<span class="function"><span class="keyword">public</span> Role <span class="title">select</span><span class="params">(Object id)</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Observer)</span><br><span class="line">	<span class="function"><span class="keyword">public</span> Collection&lt;Role&gt; <span class="title">selectAll</span><span class="params">(Role t)</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Observer)</span><br><span class="line">	<span class="function"><span class="keyword">public</span> Role <span class="title">selectOne</span><span class="params">(Role t)</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Observer)</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">count</span><span class="params">(Role t)</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">insert</span><span class="params">(Role t)</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Trigger)</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">update</span><span class="params">(Role t)</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Trigger)</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">updatePersistent</span><span class="params">(Role t)</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@CacheAnnotation</span>(role = CacheRoleType.Trigger)</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">delete</span><span class="params">(Role t)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>（如上可见，当前系统中 Role 对象已无父对象，也就是 Role 不是任何对象的观察者，因此它的 <code>@CacheRoleAnnotation</code> 中的 <code>ObserverClass</code> 为空，但在它的方法中，仍然建议将具有观察能力的方法上的注解写全，这样当您以后扩展项目需要为 Role 类时增加父对象时，只需要在 <code>@CacheRoleAnnotation</code> 中的 <code>ObserverClass</code> 上增加类名即可，不需要再修改方法上面的注解）</p>
<p>当您所有的 <i>pojo_mapper</i>.java 都按以上情况配置好后，flying 优化 mybatis 二级缓存的工作就完成了，现在您可以放心的使用 mybatis 的自带缓存，而不用担心任何缓存与数据库不匹配的问题。</p>
<h2 id="跨数据源缓存"><a href="#跨数据源缓存" class="headerlink" title="跨数据源缓存"></a><a href="#跨数据源缓存">跨数据源缓存</a></h2><p>flying采用真实数据源配合自定义 TypeHandler 的方式来实现跨数据源，并可以将多个数据源的数据保存至同一缓存中，这一切都是自动完成的，您只需要给每个 <i>pojo_mapper</i>.java 都配置上合适的注解就能实现这一点，最终在缓存层面实现数据统一。</p>
<p>有一点需要注意的是，在跨数据源且使用二级缓存时，您在 <i>pojo_mapper</i>.xml 中的 resultMap 只能以 typeHandler 方式实现多对一，而不能以 association 方式。关于这一点，您可以阅读 <a href="http://flying-doc.limeng32.com/2017/05/01/2017-05-01-%E4%BD%BF%E7%94%A8flying%E8%A7%A3%E5%86%B3pojo%E8%87%AA%E5%8A%A8%E6%98%A0%E5%B0%84%E9%97%AE%E9%A2%98/#%E8%B7%A8%E6%95%B0%E6%8D%AE%E6%BA%90">《使用 flying 解决 pojo 自动映射问题》中的跨数据源</a> 部分。</p>
<h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a><a href="#注意事项">注意事项</a></h2><h3 id="flying-如何判断缓存是否命中"><a href="#flying-如何判断缓存是否命中" class="headerlink" title="flying 如何判断缓存是否命中"></a><a href="#flying-如何判断缓存是否命中">flying 如何判断缓存是否命中</a></h3><p>flying 的缓存的 key 值是由查询条件 pojo 按业务属性序列化后再取 md5 生成，这样可保证不同线程上相同的查询条件 pojo 能够互相命中。因此使用缓存插件的前提条件是使用了<b>我们上一节介绍的 pojo 自动映射插件</b>，缓存插件无法单独使用。</p>
<h3 id="flying-已回滚的操作是否会生成缓存"><a href="#flying-已回滚的操作是否会生成缓存" class="headerlink" title="flying 已回滚的操作是否会生成缓存"></a><a href="#flying-已回滚的操作是否会生成缓存">flying 已回滚的操作是否会生成缓存</a></h3><p>不会生成。因为在您正确设计事务的情况下，未提交的（即被回滚）的数据变化是不会被查询到的，没有查询就不会生成缓存，所以您的缓存永远会和您的数据库保持一致，您可以放心的在有事务逻辑的系统中使用 flying 优化过的 mybatis 二级缓存。</p>
<h2 id="附录"><a href="#附录" class="headerlink" title="附录"></a><a href="#附录">附录</a></h2><p><a id="flying-demo2"></a></p>
<h3 id="代码示例"><a href="#代码示例" class="headerlink" title="代码示例"></a><a href="#代码示例">代码示例</a></h3><p>为了您更方便的使用 flying 进行优化 mybatis 二级缓存，我们提供了一个 <a href="https://github.com/limeng32/flying-demo2/tree/use-flying-0.9.3" target="_blank" rel="noopener">跨数据源并使用二级缓存的代码示例</a> 供您查看。<br><a id="AccountTableCreater"></a></p>
<h3 id="account-表建表语句"><a href="#account-表建表语句" class="headerlink" title="account 表建表语句"></a><a href="#account-表建表语句">account 表建表语句</a></h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="keyword">account</span> (</span><br><span class="line">  account_id <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT,</span><br><span class="line">  <span class="keyword">name</span> <span class="built_in">varchar</span>(<span class="number">20</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  address <span class="built_in">varchar</span>(<span class="number">100</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  fk_role_id <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  fk_second_role_id <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span> (account_id)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p><a id="RoleTableCreater"></a></p>
<h3 id="role-表建表语句"><a href="#role-表建表语句" class="headerlink" title="role 表建表语句"></a><a href="#role-表建表语句">role 表建表语句</a></h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="keyword">role</span> (</span><br><span class="line">  role_id <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT,</span><br><span class="line">  role_name <span class="built_in">varchar</span>(<span class="number">30</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span> (role_id)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>  
	</div>
		<footer class="article-footer clearfix">
<div class="article-catetags">

<div class="article-categories">
  <span></span>
  <a class="article-category-link" href="/categories/blog/">blog</a>
</div>


</div>



	<div class="article-share" id="share">
	
	  <div data-url="http://flying-doc.limeng32.com/2017/04/01/2017-04-01-使用flying完善Mybatis自带的二级缓存/" data-title="使用 flying 完善Mybatis 自带的二级缓存 | my bat is flying in sunny-spring" data-tsina="" class="share clearfix">
	  </div>
	
	</div>


</footer>

   	       
	</article>
	
<nav class="article-nav clearfix">
 
 <div class="prev" >
 <a href="/2017/04/15/2017-04-15-flying-阳春 新增特性/" title="flying-阳春 新增内容">
  <strong>上一篇：</strong><br/>
  <span>
  flying-阳春 新增内容</span>
</a>
</div>


<div class="next">
<a href="/2017/03/01/2017-03-01-flying的发展方向/"  title="flying 的发展方向">
 <strong>下一篇：</strong><br/> 
 <span>flying 的发展方向
</span>
</a>
</div>

</nav>

	



</div>  
      <div class="openaside"><a class="navbutton" href="#" title="顯示側邊欄"></a></div>

  <div id="toc" class="toc-aside">
  <strong class="toc-title">文章目錄</strong>
 
 <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#上手指南"><span class="toc-text"><a href="#&#x4E0A;&#x624B;&#x6307;&#x5357;" class="headerlink" title="&#x4E0A;&#x624B;&#x6307;&#x5357;"></a><a href="#&#x4E0A;&#x624B;&#x6307;&#x5357;">&#x4E0A;&#x624B;&#x6307;&#x5357;</a></span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#观察者-amp-触发者"><span class="toc-text"><a href="#&#x89C2;&#x5BDF;&#x8005;-amp-&#x89E6;&#x53D1;&#x8005;" class="headerlink" title="&#x89C2;&#x5BDF;&#x8005; &amp; &#x89E6;&#x53D1;&#x8005;"></a><a href="#&#x89C2;&#x5BDF;&#x8005;-amp-&#x89E6;&#x53D1;&#x8005;">&#x89C2;&#x5BDF;&#x8005; &amp; &#x89E6;&#x53D1;&#x8005;</a></span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#跨数据源缓存"><span class="toc-text"><a href="#&#x8DE8;&#x6570;&#x636E;&#x6E90;&#x7F13;&#x5B58;" class="headerlink" title="&#x8DE8;&#x6570;&#x636E;&#x6E90;&#x7F13;&#x5B58;"></a><a href="#&#x8DE8;&#x6570;&#x636E;&#x6E90;&#x7F13;&#x5B58;">&#x8DE8;&#x6570;&#x636E;&#x6E90;&#x7F13;&#x5B58;</a></span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#注意事项"><span class="toc-text"><a href="#&#x6CE8;&#x610F;&#x4E8B;&#x9879;" class="headerlink" title="&#x6CE8;&#x610F;&#x4E8B;&#x9879;"></a><a href="#&#x6CE8;&#x610F;&#x4E8B;&#x9879;">&#x6CE8;&#x610F;&#x4E8B;&#x9879;</a></span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#flying-如何判断缓存是否命中"><span class="toc-text"><a href="#flying-&#x5982;&#x4F55;&#x5224;&#x65AD;&#x7F13;&#x5B58;&#x662F;&#x5426;&#x547D;&#x4E2D;" class="headerlink" title="flying &#x5982;&#x4F55;&#x5224;&#x65AD;&#x7F13;&#x5B58;&#x662F;&#x5426;&#x547D;&#x4E2D;"></a><a href="#flying-&#x5982;&#x4F55;&#x5224;&#x65AD;&#x7F13;&#x5B58;&#x662F;&#x5426;&#x547D;&#x4E2D;">flying &#x5982;&#x4F55;&#x5224;&#x65AD;&#x7F13;&#x5B58;&#x662F;&#x5426;&#x547D;&#x4E2D;</a></span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#flying-已回滚的操作是否会生成缓存"><span class="toc-text"><a href="#flying-&#x5DF2;&#x56DE;&#x6EDA;&#x7684;&#x64CD;&#x4F5C;&#x662F;&#x5426;&#x4F1A;&#x751F;&#x6210;&#x7F13;&#x5B58;" class="headerlink" title="flying &#x5DF2;&#x56DE;&#x6EDA;&#x7684;&#x64CD;&#x4F5C;&#x662F;&#x5426;&#x4F1A;&#x751F;&#x6210;&#x7F13;&#x5B58;"></a><a href="#flying-&#x5DF2;&#x56DE;&#x6EDA;&#x7684;&#x64CD;&#x4F5C;&#x662F;&#x5426;&#x4F1A;&#x751F;&#x6210;&#x7F13;&#x5B58;">flying &#x5DF2;&#x56DE;&#x6EDA;&#x7684;&#x64CD;&#x4F5C;&#x662F;&#x5426;&#x4F1A;&#x751F;&#x6210;&#x7F13;&#x5B58;</a></span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#附录"><span class="toc-text"><a href="#&#x9644;&#x5F55;" class="headerlink" title="&#x9644;&#x5F55;"></a><a href="#&#x9644;&#x5F55;">&#x9644;&#x5F55;</a></span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#代码示例"><span class="toc-text"><a href="#&#x4EE3;&#x7801;&#x793A;&#x4F8B;" class="headerlink" title="&#x4EE3;&#x7801;&#x793A;&#x4F8B;"></a><a href="#&#x4EE3;&#x7801;&#x793A;&#x4F8B;">&#x4EE3;&#x7801;&#x793A;&#x4F8B;</a></span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#account-表建表语句"><span class="toc-text"><a href="#account-&#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;" class="headerlink" title="account &#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;"></a><a href="#account-&#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;">account &#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;</a></span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#role-表建表语句"><span class="toc-text"><a href="#role-&#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;" class="headerlink" title="role &#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;"></a><a href="#role-&#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;">role &#x8868;&#x5EFA;&#x8868;&#x8BED;&#x53E5;</a></span></a></li></ol></li></ol>
 
  </div>

<div id="asidepart">
<div class="closeaside"><a class="closebutton" href="#" title="隱藏側邊欄"></a></div>
<aside class="clearfix">

  <div class="linkslist">
  <p class="asidetitle">友情鏈接</p>
    <ul>
        
          <li>
            
            	<a href="https://my.oschina.net/u/2280950/blog/1580056" target="_blank" title="关于在网络应用中使用双向相关模型的研究">flying-初雪 理论基础一</a>
            
          </li>
        
          <li>
            
            	<a href="https://my.oschina.net/u/2280950/blog/1594694" target="_blank" title="或逻辑的需求的实现方案">flying-初雪 理论基础二</a>
            
          </li>
        
          <li>
            
            	<a href="http://www.cnblogs.com/dasn/articles/5208022.html" target="_blank" title="为什么我们在初雪版中引入自定义主键机制">flying-初雪 新增自定义主键生成器特性</a>
            
          </li>
        
    </ul>
</div>

</aside>
</div>
    </div>
    <footer><div id="footer" >
	
	<div class="line">
		<span></span>
		<div class="author"></div>
	</div>
	
	
	<section class="info">
		<p> God is in his heaven, all&#39;s right with the code. </p>
	</section>
	 
	<div class="social-font" class="clearfix">
		
		
		<a href="https://gitee.com/limeng32/mybatis.flying" target="_blank" class="icon-gitee" title="gitee"></a>
		
		
		<a href="https://github.com/limeng32/mybatis.flying" target="_blank" class="icon-github" title="github"></a>
		
		
		
		
		
		
		
		<a href="http://www.zhihu.com/people/li-meng-48" target="_blank" class="icon-zhihu" title="知乎"></a>
		
		
		
	</div>
			
		

	<p class="copyright">
		written by <a href="/about" target="_blank" title="limeng32">limeng32</a>
		© 2018
	</p>
</div>
</footer>
    <script src="/js/jquery-2.0.3.min.js"></script>
<script src="/js/jquery.imagesloaded.min.js"></script>
<script src="/js/gallery.js"></script>
<script src="/js/jquery.qrcode-0.12.0.min.js"></script>

<script type="text/javascript">
$(document).ready(function(){ 
  $('.navbar').click(function(){
    $('header nav').toggleClass('shownav');
  });
  var myWidth = 0;
  function getSize(){
    if( typeof( window.innerWidth ) == 'number' ) {
      myWidth = window.innerWidth;
    } else if( document.documentElement && document.documentElement.clientWidth) {
      myWidth = document.documentElement.clientWidth;
    };
  };
  var m = $('#main'),
      a = $('#asidepart'),
      c = $('.closeaside'),
      o = $('.openaside');
  c.click(function(){
    a.addClass('fadeOut').css('display', 'none');
    o.css('display', 'block').addClass('fadeIn');
    m.addClass('moveMain');
  });
  o.click(function(){
    o.css('display', 'none').removeClass('beforeFadeIn');
    a.css('display', 'block').removeClass('fadeOut').addClass('fadeIn');      
    m.removeClass('moveMain');
  });
  $(window).scroll(function(){
    o.css("top",Math.max(80,260-$(this).scrollTop()));
  });
  
  $(window).resize(function(){
    getSize(); 
    if (myWidth >= 1024) {
      $('header nav').removeClass('shownav');
    }else{
      m.removeClass('moveMain');
      a.css('display', 'block').removeClass('fadeOut');
      o.css('display', 'none');
      
      $('#toc.toc-aside').css('display', 'none');
        
    }
  });
});
</script>

<script type="text/javascript">
$(document).ready(function(){ 
  var ai = $('.article-content>iframe'),
      ae = $('.article-content>embed'),
      t  = $('#toc'),
      ta = $('#toc.toc-aside'),
      o  = $('.openaside'),
      c  = $('.closeaside');
  if(ai.length>0){
    ai.wrap('<div class="video-container" />');
  };
  if(ae.length>0){
   ae.wrap('<div class="video-container" />');
  };
  c.click(function(){
    ta.css('display', 'block').addClass('fadeIn');
  });
  o.click(function(){
    ta.css('display', 'none');
  });
  $(window).scroll(function(){
    ta.css("top",Math.max(140,320-$(this).scrollTop()));
  });
});
</script>


<script type="text/javascript">
$(document).ready(function(){ 
  var $this = $('.share'),
      url = $this.attr('data-url'),
      encodedUrl = encodeURIComponent(url),
      title = $this.attr('data-title'),
      tsina = $this.attr('data-tsina'),
      description = $this.attr('description');
  var html = [
  '<div class="hoverqrcode clearfix"></div>',
  '<a class="overlay" id="qrcode"></a>',
  '<a href="https://www.facebook.com/sharer.php?u=' + encodedUrl + '" class="article-share-facebook" target="_blank" title="Facebook"></a>',
  '<a href="https://twitter.com/intent/tweet?url=' + encodedUrl + '" class="article-share-twitter" target="_blank" title="Twitter"></a>',
  '<a href="#qrcode" class="article-share-qrcode" title="微信"></a>',
  '<a href="http://widget.renren.com/dialog/share?resourceUrl=' + encodedUrl + '&srcUrl=' + encodedUrl + '&title=' + title +'" class="article-share-renren" target="_blank" title="人人"></a>',
  '<a href="http://service.weibo.com/share/share.php?title='+title+'&url='+encodedUrl +'&ralateUid='+ tsina +'&searchPic=true&style=number' +'" class="article-share-weibo" target="_blank" title="微博"></a>',
  '<span title="Share to"></span>'
  ].join('');
  $this.append(html);

  $('.hoverqrcode').hide();

  var myWidth = 0;
  function updatehoverqrcode(){
    if( typeof( window.innerWidth ) == 'number' ) {
      myWidth = window.innerWidth;
    } else if( document.documentElement && document.documentElement.clientWidth) {
      myWidth = document.documentElement.clientWidth;
    };
    var qrsize = myWidth > 1024 ? 200:100;
    var options = {render: 'image', size: qrsize, fill: '#2ca6cb', text: url, radius: 0.5, quiet: 1};
    var p = $('.article-share-qrcode').position();
    $('.hoverqrcode').empty().css('width', qrsize).css('height', qrsize)
                          .css('left', p.left-qrsize/2+20).css('top', p.top-qrsize-10)
                          .qrcode(options);
  };
  $(window).resize(function(){
    $('.hoverqrcode').hide();
  });
  $('.article-share-qrcode').click(function(){
    updatehoverqrcode();
    $('.hoverqrcode').toggle();
  });
  $('.article-share-qrcode').hover(function(){}, function(){
      $('.hoverqrcode').hide();
  });
});   
</script>











<link rel="stylesheet" href="/fancybox/jquery.fancybox.css" media="screen" type="text/css">
<script src="/fancybox/jquery.fancybox.pack.js"></script>
<script type="text/javascript">
$(document).ready(function(){ 
  $('.article-content').each(function(i){
    $(this).find('img').each(function(){
      if ($(this).parent().hasClass('fancybox')) return;
      var alt = this.alt;
      if (alt) $(this).after('<span class="caption">' + alt + '</span>');
      $(this).wrap('<a href="' + this.src + '" title="' + alt + '" class="fancybox"></a>');
    });
    $(this).find('.fancybox').each(function(){
      $(this).attr('rel', 'article' + i);
    });
  });
  if($.fancybox){
    $('.fancybox').fancybox();
  }
}); 
</script>



<!-- Analytics Begin -->





<!-- Analytics End -->

<!-- Totop Begin -->

	<div id="totop">
	<a title="返回頂部"><img src="/img/scrollup.png"/></a>
	</div>
	<script src="/js/totop.js"></script>

<!-- Totop End -->

<!-- MathJax Begin -->
<!-- mathjax config similar to math.stackexchange -->


<!-- MathJax End -->

<!-- Tiny_search Begin -->

<!-- Tiny_search End -->

  </body>
</html>
